home *** CD-ROM | disk | FTP | other *** search
/ Delphi 2.0 - Programmer's Utilities Power Pack / Delphi 2.0 Programmer's Utilities Power Pack.iso / s_to_z / sysfile / sysfile1.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-09-15  |  34.7 KB  |  856 lines

  1. {=================================================
  2.  Program: SysFile.Exe
  3.  Version: 1.0
  4.  Module: SysFile1.pas
  5.  Author: Wayne Niddery
  6.  CopyRight ⌐ 1995 Wayne Niddery and WinWright
  7.  =================================================}
  8. unit Sysfile1;
  9.  
  10. {SysFile1 implements the mainform of the program. All events taking
  11.  place on the main form are handled here. This includes all actions
  12.  on the different pages of the TabbedNoteBook component.}
  13.  
  14. interface
  15.  
  16. uses
  17.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  18.   Forms, Dialogs, StdCtrls, TabNotBk, Buttons, ExtCtrls, Menus,
  19.   SysOpts, SysGlb;
  20.  
  21. type
  22.   {In the visual designer, The TabbedNotebook component was given a
  23.    single page called 'Set Up'. All controls defined below (by Delphi)
  24.    are on this first page of the Notebook with the exception of the
  25.    menu components and Find/Replace dialog components (which are always
  26.    directly on the form)}
  27.   TForm1 = class(TForm)
  28.     Notebook: TTabbedNotebook;
  29.     BottomPanel: TPanel;             {Various panel components for both}
  30.     Panel2: TPanel;             {appearance and an aid to placing}
  31.     BtnPanel: TPanel;           {the other controls when the form}
  32.     FilePanel: TPanel;          {needs to change its size}
  33.     AutoPanel: TPanel;
  34.     FileList: TListBox;         {FileList displays available files}
  35.     AutoList: TListBox;         {AutoList displays opened files}
  36.     FileLabel: TLabel;
  37.     AutoLabel: TLabel;
  38.     bTake: TBitBtn;             {Copies file names to AutoList}
  39.     bDrop: TBitBtn;             {Removes file names from AutoList}
  40.     bOptions: TBitBtn;          {Executes the Options Dialog}
  41.     bHelp: TBitBtn;             {Executes the help system}
  42.     EditPopup: TPopupMenu;      {Popup menu for TMemo controls}
  43.     mFind: TMenuItem;           {Executes Find dialog}
  44.     mReplace: TMenuItem;        {Executes Replace dialog}
  45.     N1: TMenuItem;
  46.     mEditRemove: TMenuItem;     {Close file & remove from auto list}
  47.     mEditProtect: TMenuItem;    {Add file to protection list}
  48.     N3: TMenuItem;
  49.     mEditDelete: TMenuItem;     {Delete this file}
  50.     N5: TMenuItem;
  51.     mExit: TMenuItem;           {Exit the application}
  52.     AutoPopup: TPopupMenu;      {Popup for the Auto Listbox}
  53.     mAutoEdit: TMenuItem;
  54.     mAutoRemove: TMenuItem;
  55.     mAutoProtect: TMenuItem;
  56.     N2: TMenuItem;
  57.     mAutoDelete: TMenuItem;
  58.     FilePopup: TPopupMenu;      {Popup for the File Listbox}
  59.     mFileEdit: TMenuItem;
  60.     mFileAdd: TMenuItem;
  61.     mFileProtect: TMenuItem;
  62.     N4: TMenuItem;
  63.     mFileDelete: TMenuItem;
  64.     FindDialog1: TFindDialog;
  65.     ReplaceDialog1: TReplaceDialog;
  66.     procedure FormCreate(Sender: TObject);
  67.     procedure bTakeClick(Sender: TObject);
  68.     procedure bDropClick(Sender: TObject);
  69.     procedure NotebookClick(Sender: TObject);
  70.     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  71.     procedure FormResize(Sender: TObject);
  72.     procedure FormKeyUp(Sender: TObject; var Key: Word;
  73.       Shift: TShiftState);
  74.     procedure ListDragOver(Sender, Source: TObject; X, Y: Integer;
  75.       State: TDragState; var Accept: Boolean);
  76.     procedure ListMouseDown(Sender: TObject; Button: TMouseButton;
  77.       Shift: TShiftState; X, Y: Integer);
  78.     procedure FileListDragDrop(Sender, Source: TObject; X, Y: Integer);
  79.     procedure AutoListDragDrop(Sender, Source: TObject; X, Y: Integer);
  80.     procedure mDeleteClick(Sender: TObject);
  81.     procedure mEditRemoveClick(Sender: TObject);
  82.     procedure mEditClick(Sender: TObject);
  83.     procedure mExitClick(Sender: TObject);
  84.     procedure mFindClick(Sender: TObject);
  85.     procedure mReplaceClick(Sender: TObject);
  86.     procedure ReplaceDialog1Replace(Sender: TObject);
  87.     procedure bOptionsClick(Sender: TObject);
  88.     procedure ProtectClick(Sender: TObject);
  89.     procedure FindDialog1Find(Sender: TObject);
  90.     procedure bHelpClick(Sender: TObject);
  91.   private
  92.     { Private declarations }
  93.     BtnPanelWidth: integer;           {used to help resize form}
  94.     FormHeight: integer;              {use design-time height as minimum}
  95.     procedure LoadIniFiles;
  96.     procedure LoadAutoFiles;
  97.     procedure MakePage(pg: integer);
  98.     procedure AdjustSizes;
  99.     function CheckSave(pg: integer; ask: Boolean): integer;
  100.     function DoFind(Sender: TFindDialog): Boolean;
  101.     procedure WMGetMinMaxInfo(var msg: TWMGetMinMaxInfo); message wm_GetMinMaxInfo;
  102.     procedure WMDelOptChanged(var msg: TMessage); message wm_user + DelOptChanged;
  103.   public
  104.     { Public declarations }
  105.   end;
  106.  
  107. var
  108.   Form1: TForm1;
  109.  
  110. implementation
  111.  
  112. {$R *.DFM}
  113.  
  114. uses
  115.   {SysProt implements the protection list.
  116.    IniFiles is a Delphi unit that makes .INI management easier
  117.    SysGlb stores the global constants and vars needed}
  118.   SysProt, IniFiles;
  119.  
  120. {FormCreate responds to the OnCreate event}
  121. procedure TForm1.FormCreate(Sender: TObject);
  122. begin
  123.   {The OnRestore event is owned by the Application component.
  124.    This event fires when the application is restored from a
  125.    minimized or maximized state. It is being directed to the
  126.    form's OnResize event handler.}
  127.   Application.OnRestore := FormResize;
  128.   {Save width of button panel to help in controlling appearance}
  129.   BtnPanelWidth := BtnPanel.Width;
  130.   {Call private methods to load the FileList and AutoList listboxes
  131.    and open the files in AutoList}
  132.   LoadIniFiles;
  133.   LoadAutoFiles;
  134.   {Read size and position info from INI file and set the main
  135.    form accordingly}
  136.   with SysIni do begin
  137.     SetBounds(
  138.       ReadInteger(iniPosition, iniLeft, Left),
  139.       ReadInteger(iniPosition, iniTop, Top),
  140.       ReadInteger(iniPosition, iniWidth, Width),
  141.       ReadInteger(iniPosition, iniHeight, Height));
  142.     {Read page last displayed so it displays first}
  143.     with NoteBook do
  144.       PageIndex := ReadInteger(iniPosition, iniPage, PageIndex);
  145.   end;
  146. {  AdjustSizes;}
  147. end;
  148.  
  149. {Load the names of all .INI files found in the Windows directory
  150.  plus AUTOEXEC.BAT and CONFIG.SYS}
  151. procedure TForm1.LoadIniFiles;
  152. var
  153.   ret: integer;
  154.   SRec: TSearchRec;
  155. begin
  156.   {The FileList listbox is first set to sorted state so .INI files
  157.    will display in alphabetical order}
  158.   FileList.Sorted := True;
  159.   {FindFirst will locate the first INI file. 'faAnyFile' tells Windows
  160.    we want all INI files even if hidden or system attributes are on}
  161.   ret := FindFirst(WinDir + IniFilter, faAnyFile, SRec);
  162.   {Both FindFirst and FindNext return 0 when no more files are found}
  163.   while ret=0 do begin
  164.     FileList.Items.Add(Srec.Name);  {Add file name to listbox}
  165.     ret := FindNext(SRec);          {get the next file name}
  166.   end;
  167.   {The sorted state is now turned off so we can force AUTOEXEC.BAT
  168.    and CONFIG.SYS to the top of the list}
  169.   FileList.Sorted := False;
  170.   FileList.Items.Insert(0, AutoExec);
  171.   FileList.Items.Insert(1, Config);
  172. end;
  173.  
  174. {LoadAutoFiles reads the list of files to be opened from the .INI
  175.  file. If this file hasn't been created yet then it gets a  default
  176.  list of files to open from the Protection list in SysProt.  This
  177.  default list was defined in the TMemo component of the SysProt
  178.  form at design time. For each file opened, a page is added to the
  179.  notebook component, a TMemo component is added to the page and the
  180.  file is then loaded into the TMemo ready for viewing / editing}
  181. procedure TForm1.LoadAutoFiles;
  182. var
  183.   i, pos: integer;
  184.   s: string;
  185. begin
  186.   {Initialize AutoList}
  187.   if not FileExists(SysIni.FileName) then
  188.     {copy from default list, but don't include SYSFILE.INI}
  189.     for i := 0 to High(DefProt)-1 do
  190.       AutoList.Items.Add(Defprot[i])
  191.   else begin
  192.     {read file names from INI file}
  193.     i := 0;
  194.     repeat
  195.       {the variable i is used as a key to get each file name from
  196.        the .INI file. When there are no more strings, a string of
  197.        zero length is returned}
  198.       s := SysIni.ReadString(iniAutoOpen, IntToStr(i), '');
  199.       if Length(s) > 0 then AutoList.Items.Add(s); {add to listbox}
  200.       inc(i); {next INI key}
  201.     until Length(s) = 0;
  202.   end;
  203.   with NoteBook do begin
  204.     {create a page for each selected file name}
  205.     for i := 0 to AutoList.Items.Count - 1 do begin
  206.       Pages.Insert(i, AutoList.Items[i]); {add new page for file name}
  207.       MakePage(i);        {add a memo and load selected file into it}
  208.     end;
  209.     PageIndex := 0; {Focus first selected file. This may be changed
  210.                      after returning to the FormCreate method}
  211.   end;
  212. end;
  213.  
  214. {MakePage does the work of creating and initializing a TMemo
  215.  component to be placed on a notebook page. The ofs parameter
  216.  is the page number to place the TMemo on. It has been coded in
  217.  a separate procedure because it needs to be used by a few other
  218.  methods in the application}
  219. procedure TForm1.MakePage(pg: integer);
  220. var
  221.   Memo: TMemo;
  222. begin
  223.   {create a new TMemo with Form1 as the owner}
  224.   Memo := TMemo.Create(Form1);
  225.   with Memo do begin
  226.     {the Page component of the notebook will be parent of the TMemo}
  227.     Parent := TTabPage(NoteBook.Pages.Objects[pg]);
  228.     Align := alClient;      {fill entire page}
  229.     ScrollBars := ssBoth;   {display both scroll bars}
  230.     TabStop := True;        {let it receive focus}
  231.     WordWrap := False;      {don't wrap lines of these files}
  232.     PopupMenu := EditPopup; {assign a popup menu}
  233.     HideSelection := False; {always allow selected text to show}
  234.     HelpContext := Editing_a_File; {context help on F1}
  235.     {save file name in Hint property for easy access}
  236.     Hint := NoteBook.Pages.Strings[pg];
  237.     {construct a complete file path in the Hint property}
  238.     if (Hint = AutoExec) or (Hint = Config) then Hint := Root + Hint
  239.     else Hint := WinDir + Hint;
  240.     Lines.LoadFromFile(Hint); {open and read in the file}
  241.   end;
  242. end;
  243.  
  244. {The form is resizeable by the user and, whenever a page is added
  245.  or removed from the notebook component, the notebook component may
  246.  grow or shrink in size. The AdjustSizes method, called by other
  247.  methods such as FormResize, adjusts the width of the Panel components
  248.  containing the list boxes so they stay proportional within the form}
  249. procedure TForm1.AdjustSizes;
  250. var
  251.   space: integer;
  252.   pt: TPoint;
  253. begin
  254.   with NoteBook do begin
  255.     {try to keep tabs from gettng to short for the file names}
  256.     TabsPerRow := Form1.Width div 100;
  257.     {Get current width of notebook page. The Objects property contains
  258.      a set of TTabPage components which represent each of the pages.
  259.      The Objects property itself is of type TObject, so in order to
  260.      access the properties of TTabPage, Objects[x] must be typecasted
  261.      as a TTabPage}
  262.     space := TTabPage(Pages.Objects[Pages.Count-1]).Width;
  263.     {subtract width of panel containing buttons and divide in half}
  264.     space := (space - BtnPanelWidth) div 2;
  265.     {allocate equal space to both list box panels}
  266.     FilePanel.Width := space;
  267.     AutoPanel.Width := space;
  268.     {adjust label to line up with AutoList listbox}
  269.     AutoLabel.Left := AutoPanel.Left;
  270.   end;
  271.   {Adjust form height so all buttons remain visible}
  272.   pt := bHelp.ClientToScreen(Point(0, bHelp.Height));
  273.   space := pt.y - Top + BottomPanel.Height + 16;
  274.   {allow height to be larger than needed}
  275.   if space > Height then Height := space;
  276. end;
  277.  
  278. {CheckSave is call by other routines to check whether a file has been
  279.  modified. If it has, the user is prompted if necessary and the
  280.  file is saved if so indicated}
  281. function TForm1.CheckSave(pg: integer; ask: Boolean): integer;
  282. begin
  283.   Result := mrNo; {assume no modifications yet}
  284.   {address the specified TMemo}
  285.   with TMemo(TTabPage(NoteBook.Pages.Objects[pg]).Controls[0]) do begin
  286.     if Modified then begin {changes made}
  287.       if ask then begin {let user decide if prompt option set}
  288.         if (OptionsDlg.SaveGroup.ItemIndex = opPromptEach) then
  289.           Result := MessageDlg(Hint + ' has changed. Save?',
  290.             mtConfirmation, mbYesNoCancel, 0)
  291.         else if (OptionsDlg.SaveGroup.ItemIndex = opPromptOnce) then
  292.           Result := MessageDlg('One or more files have changed. Save?',
  293.             mtConfirmation, mbYesNoCancel, 0)
  294.         else Result := mrYes; {auto save the file}
  295.       end;
  296.       if Result = mrCancel then Exit {cancel request}
  297.       else if Result = mrYes then Lines.SaveToFile(Hint); {save file}
  298.     end;
  299.   end;
  300. end;
  301.  
  302. {This procedure handles the OnClick event of the NoteBook component.
  303.  When the notebook tabs are clicked, the notebook flips to the selected
  304.  page but it then remains the focused control. We want one of the controls
  305.  on the notebook page to get focus. For most, this is always just the
  306.  single TMemo component. For the Setup page, it can be any component
  307.  except the TLabels. TLabels can't get focus (and so don't have a SetFocus
  308.  method). What TLabels do have though is a FocusControl property that can
  309.  be set to associate the TLabel with some other component. This was done
  310.  at design time for the two labels that name the listboxes}
  311. procedure TForm1.NoteBookClick(Sender: TObject);
  312. begin
  313.   with NoteBook, NoteBook.Pages do begin
  314.     if TTabPage(Objects[PageIndex]).Controls[0] is TLabel then
  315.       TLabel(TTabPage(Objects[PageIndex]).Controls[0]).FocusControl.SetFocus
  316.     else {whether 1st control is TMemo, TListBox, or TButton...}
  317.       TWinControl(TTabPage(Objects[PageIndex]).Controls[0]).SetFocus;
  318.   end;
  319.   AdjustSizes; {In case Set Up selected and form previously resized}
  320. end;
  321.  
  322. {This handles clicks on the Take button and the Add menu item. It loops
  323.  through each file name selected (highlighted) in the FileList listbox,
  324.  adds it to the AutoList listbox, adds a page for it to the notebook and
  325.  calls MakePage to create a TMemo and load in the selected file. When a
  326.  new page is added to a TabbedNoteBook component, focus is given to the
  327.  new page. Once all pages have been added, we want focus to return to the
  328.  Setup page. To do this, the name of the setup page is first saved (because
  329.  its page index value will change while adding other pages) so it can be
  330.  assigned as the active page at the end. Also, if more than one page is
  331.  being added, the notebook is hidden during the process. This makes the
  332.  process much faster and the appearance cleaner and more professional
  333.  looking}
  334. procedure TForm1.bTakeClick(Sender: TObject);
  335. var
  336.   i, idx: integer;
  337.   ap: string[12];
  338. begin
  339.   if FileList.SelCount > 0 then begin       {see if any selected}
  340.     for i := 0 to FileList.Items.Count-1 do {loop through items}
  341.       if FileList.Selected[i] then begin    {see if selected}
  342.         if FileList.SelCount > 1 then NoteBook.Hide; {hide process}
  343.         {skip if already in auto list}
  344.         if AutoList.Items.IndexOf(FileList.Items[i]) < 0 then begin
  345.           idx := AutoList.Items.Add(FileList.Items[i]); {add to auto list}
  346.           with NoteBook do begin            {add new page}
  347.             {insert at last position - pushes SetUp down}
  348.             Pages.Insert(idx, FileList.Items[i]);
  349.             MakePage(idx);
  350.           end;
  351.         end;
  352.       end;
  353.     AdjustSizes;  {adjust setup page properly}
  354.     NoteBook.PageIndex := NoteBook.Pages.Count - 1; {back to Setup page}
  355.     if not NoteBook.Showing then NoteBook.Show; {display again}
  356.     {Send Windows message to FileList listbox to clear selected items}
  357.     SendMessage(FileList.Handle, lb_SelItemRange, 0, FileList.Items.Count);
  358.   end;
  359. end;
  360.  
  361. {This handles the clicks on the Drop button and the Remove menu item in
  362.  the popup menus. The logic is basically the same as above for the
  363.  Take button, but is simpler}
  364. procedure TForm1.bDropClick(Sender: TObject);
  365. var
  366.   i, idx: integer;
  367.   NotAsked: Boolean;
  368. begin
  369.   NotAsked := True;
  370.   if AutoList.SelCount > 0 then begin             {see if any selected}
  371.     if AutoList.SelCount > 1 then NoteBook.Hide;  {hide process}
  372.     for i := AutoList.Items.Count-1 downto 0 do begin {loop through items}
  373.       if AutoList.Selected[i] then begin          {see if selected}
  374.         {find page index in notebook}
  375.         idx := NoteBook.GetIndexForPage(AutoList.Items[i]);
  376.         {check if file modified and save if necessary}
  377.         if CheckSave(idx, NotAsked) = mrCancel then break;
  378.         if OptionsDlg.SaveGroup.ItemIndex = opPromptOnce then
  379.           NotAsked := False;  {so CheckSave won't prompt again}
  380.         {Because the Parent property of the TMemo component on this page
  381.          was assigned to the page (TTabSet component), it will be
  382.          destroyed for us by the TTabset component when we destroy the
  383.          TTabset in the next statement}
  384.         NoteBook.Pages.Delete(idx);       {delete notebook page}
  385.         AutoList.Items.Delete(i);         {delete from Autolist}
  386.       end;
  387.     end;
  388.     AdjustSizes;
  389.     if not NoteBook.Showing then NoteBook.Show;
  390.   end;
  391. end;
  392.  
  393. {FormCloseQuery checks each open file to see if it has been modified.
  394.  If so, it is saved. If one of the two prompt options is checked then
  395.  a message is displayed accordingly (either for each modified file or
  396.  once for the first modified file. If prompting for each, user has the
  397.  option to skip saving any particular file. For both prompt options,
  398.  user can cancel the Exit request and return to the application. If
  399.  user does not cancel, then after modified files are saved, the INI
  400.  file is updated. User is then given option to reboot system (if
  401.  AUTOEXEC.BAT or CONFIG.SYS is modified) or restarting windows (if one
  402.  or more INI files have been changed but not AUTOEXEC OR CONFIG)}
  403. procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  404. var
  405.   i, idx, mods, ret: integer;
  406.   NotAsked, Boot: boolean;
  407.   rw: longint; {simply a place holder for ExitWindows API call}
  408.   strz: array [0..145] of char; {temp asciiz string to close help}
  409.  
  410. begin
  411.   mods := 0;
  412.   CanClose := False;
  413.   Boot := False;
  414.   NotAsked := True;
  415.   {memos are owned by the form, so look for them}
  416.   for i := 0 to AutoList.Items.Count - 1 do begin
  417.     idx := NoteBook.GetIndexForPage(AutoList.Items[i]);
  418.     ret := CheckSave(idx, NotAsked);
  419.     if OptionsDlg.SaveGroup.Itemindex = opPromptOnce then
  420.       NotAsked := False;
  421.     if ret = mrCancel then exit
  422.     else if ret = mrNo then continue;
  423.     Inc(mods); {count number saved}
  424.     {see if a boot is called for}
  425.     if (Pos(AutoExec, AutoList.Items[i]) > 0)
  426.     or (Pos(Config, AutoList.Items[i]) > 0) then Boot := True
  427.     else
  428.   end;
  429.  
  430.   {Erase AutoSave section in INI file, then save names of open files}
  431.   SysIni.EraseSection(iniAutoOpen);
  432.   for i := 0 to AutoList.Items.Count -1 do
  433.     SysIni.WriteString(iniAutoOpen, IntToStr(i), AutoList.Items[i]);
  434.   {save the position and size of the form and the current page}
  435.   with SysIni do begin
  436.     WriteInteger(iniPosition, iniLeft, Left);
  437.     WriteInteger(iniPosition, iniTop, Top);
  438.     WriteInteger(iniPosition, iniWidth, Width);
  439.     WriteInteger(iniPosition, iniHeight, Height);
  440.     WriteInteger(iniPosition, iniPage, NoteBook.PageIndex);
  441.   end;
  442.  
  443.   {make sure the help file gets closed}
  444.   StrPCopy(strz, Application.HelpFile);
  445.   WinHelp(Application.Handle, strz, help_Quit, 0);
  446.  
  447.   CanClose := True; {proceed with program termination}
  448.   if Boot then begin {boot is called for}
  449.     if MessageDlg(RebootMsg, mtConfirmation, [mbYes, mbNo], 0) = mrYes
  450.     then ExitWindows(ew_RebootSystem, rw);
  451.   end
  452.   else if mods>0 then begin {Windows restart is called for}
  453.     if MessageDlg(RestartMsg, mtConfirmation, [mbYes, mbNo], 0) = mrYes
  454.     then ExitWindows(ew_RestartWindows, rw);
  455.   end;
  456. end;
  457.  
  458. {This handles the OnResize event. All that's required is to adjust
  459.  the components on the Setup page}
  460. procedure TForm1.FormResize(Sender: TObject);
  461. begin
  462.   AdjustSizes;
  463. end;
  464.  
  465. {This handles the OnKeyUp event. Here we allow the Delete and Insert
  466.  keys to be used to add or remove selected files to/from AutoList.
  467.  NOTE: The KeyPreview property must be set True for the form in order
  468.  to receive OnClick events for control keys such as these}
  469. procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;
  470.   Shift: TShiftState);
  471. begin
  472.   if key = vk_Delete then bDrop.Click       {fake click on Drop button}
  473.   else if key = vk_Insert then bTake.Click; {fake click on Take button}
  474. end;
  475.  
  476. {OnMouseDown handler for FileList and AutoList list boxes. If option
  477.  for Drag 'n Drop is on, then start the drag. DragMode for the
  478.  list boxes must be set to dmManual so we can decide here. The reason
  479.  drag and drop is an option is because without it, user can drag mouse
  480.  to highlight multiple entries in the listbox.}
  481. procedure TForm1.ListMouseDown(Sender: TObject; Button: TMouseButton;
  482.   Shift: TShiftState; X, Y: Integer);
  483. begin
  484.   if OptionsDlg.ckUseDrag.Checked then
  485.     (Sender as TListBox).BeginDrag(True);
  486. end;
  487.  
  488. {This handles the OnDragOver event. The Take and Drop buttons and both
  489.  listboxes have their OnDragOver events assigned to this method.
  490.  Sender is the component the mouse is currently over.
  491.  Source is the component where dragging originated.
  492.  We need to determine whether the component being dragged is allowed
  493.  to be dropped on the component currently under the mouse.
  494.  The combinations are:
  495.  1. Since only the two list boxes can be dragged, it's always valid to
  496.     drop on a listbox (you must allow a component to "drop" on itself
  497.     in order for the proper cursor to appear when dragging starts).
  498.  2. The FileList can drop on the Take button
  499.  3. The AutoList can drop on the Drop button}
  500. procedure TForm1.ListDragOver(Sender, Source: TObject; X, Y: Integer;
  501.   State: TDragState; var Accept: Boolean);
  502. begin
  503.   if (Sender is TListBox) {itself or other list box is OK}
  504.   or ((Source = FileList) and (Sender = bTake))
  505.   or ((Source = AutoList) and (Sender = bDrop))
  506.   then begin
  507.     Accept :=True; {allow the drop if mouse is released over Sender}
  508.     {Just a nice touch here; see if single or multiple file names
  509.      are selected and display the appropriate cursor}
  510.     if (Source as TListBox).SelCount > 1 then
  511.       (Source as TListBox).DragCursor := crMultiDrag
  512.     else
  513.       (Source as TListBox).DragCursor := crDrag;
  514.   end
  515.   else Accept := False; {combination not valid for drop}
  516. end;
  517.  
  518. {Handles OnDragDrop event for FileList. The only check that needs to
  519.  be made here is that user is not attempting to drop on the same
  520.  listbox s/he dragged from. All other valid combinations are checked
  521.  in the OnDragDrop handler}
  522. procedure TForm1.FileListDragDrop(Sender, Source: TObject; X, Y: Integer);
  523. begin
  524.   if OptionsDlg.ckUseDrag.Checked then  {fake click on Drop button}
  525.     if Sender <> Source then bDrop.Click;
  526. end;
  527.  
  528. {Handles OnDragDrop for AutoList. Same check as for FileList}
  529. procedure TForm1.AutoListDragDrop(Sender, Source: TObject; X, Y: Integer);
  530. begin
  531.   if OptionsDlg.ckUseDrag.Checked then  {fake click on Take button}
  532.     if Sender <> Source then bTake.Click;
  533. end;
  534.  
  535. {This method is called when the Delete menu item is selected from any
  536.  of the three popup menus. They must be handled differently depending
  537.  on which popup it was selected from}
  538. procedure TForm1.mDeleteClick(Sender: TObject);
  539. var
  540.   sel, idx, ret: integer;
  541.   path: string;
  542.   List: TListBox;
  543.  
  544.   {Because only the actual file name is stored in the list boxes and
  545.    on the Tabs of the Notebook pages, GetName is needed to construct
  546.    the complete path name}
  547.   procedure GetName;
  548.   begin
  549.     if Sender = mEditDelete then begin {one of the TMemos}
  550.       path := TTabPage(NoteBook.Pages.Objects[NoteBook.PageIndex])
  551.         .Controls[0].Hint; {get full path name}
  552.     end
  553.     else begin  {one of the list boxes}
  554.       path := List.Items[sel]; {List was set before calling GetName}
  555.       if (path = AutoExec) or (path = Config) then {build path}
  556.         path := Root + path
  557.       else
  558.         path := WinDir + path;
  559.     end;
  560.   end;
  561.  
  562.   {CanDelete checks the protection list to determine whether ot not
  563.    the currently selected file name can be deleted. If currently
  564.    protected, user is informed that deletion will not occur}
  565.   function CanDelete(const name: string): boolean;
  566.   begin
  567.     with ProtectDlg.ProtList.Lines do
  568.       Result := IndexOf(ExtractFileName(name)) < 0; {look for file name}
  569.     if not Result then {yes, it's in protect list}
  570.       Messagebeep(mb_IconInformation);
  571.       MessageDlg(name + ' is in the protection list and will not be deleted',
  572.         mtInformation, [mbOK], 0);
  573.   end;
  574.  
  575. begin
  576.   {if No Delete option selected then just ignore request. Note that
  577.    this check should be redundant as we are disabling the the Delete
  578.    menu items when the No Delete radio button is set}
  579.   if OptionsDlg.DeleteGroup.ItemIndex = opNoDelete then exit;
  580.  
  581.   if Sender = mEditDelete then begin {from a TMemo}
  582.     GetName; {construct full path name}
  583.     if CanDelete(path) then begin {make sure file not protected}
  584.       {get confirmation if Confirm option set}
  585.       if OptionsDlg.DeleteGroup.ItemIndex = opConfirm then begin
  586.         MessageBeep(mb_IconQuestion);
  587.         ret := MessageDlg('Are you sure you want to delete ' + path,
  588.           mtWarning, [mbYes, mbNo], 0);
  589.         if ret = mrNo then Exit; {user had second thoughts}
  590.       end;
  591.       DeleteFile(path); {physically delete the file from disk}
  592.       {remove file name from Autolist and FileList}
  593.       AutoList.Items.Delete(AutoList.Items.IndexOf(NoteBook.ActivePage));
  594.       FileList.Items.Delete(FileList.Items.IndexOf(NoteBook.ActivePage));
  595.       {remove page from notebook}
  596.       NoteBook.Pages.Delete(NoteBook.PageIndex);
  597.     end;
  598.   end else begin {from a list box}
  599.     {setting a local variable to the list box responsible for the
  600.      delete request simplifies the code}
  601.     if (Sender = mFileDelete) then List := FileList
  602.     else List := AutoList;
  603.     {loop through file names in whichever list sent request}
  604.     for sel := List.Items.Count - 1 downto 0 do
  605.       if List.Selected[sel] then begin  {a highlighted file name}
  606.         GetName; {construct full path from this file name}
  607.         if not CanDelete(path) then continue;  {check if protected}
  608.         if OptionsDlg.DeleteGroup.ItemIndex = opConfirm then begin
  609.           MessageBeep(mb_IconQuestion);
  610.           ret := MessageDlg('Are you sure you want to delete ' +
  611.             path, mtWarning, mbYesNoCancel, 0);
  612.           if ret = mrNo then continue {try next file name}
  613.           else if ret = mrCancel then break; {skip remaining selects}
  614.         end else
  615.           if List.SelCount > 1 then NoteBook.Hide; {faster, looks better}
  616.         DeleteFile(path); {physically delete from disk}
  617.         if Sender = mAutoDelete then begin
  618.           {find corresponding file name in notebook pages and in
  619.            FileList and remove}
  620.           FileList.Items.Delete(FileList.Items.IndexOf(AutoList.Items[sel]));
  621.           NoteBook.Pages.Delete(Notebook.GetIndexForPage(AutoList.Items[sel]));
  622.           AutoList.Items.Delete(sel); {remove from AutoList}
  623.         end
  624.         else begin {called from FileList}
  625.           {see if file name exists in AutoList}
  626.           idx := AutoList.Items.IndexOf(FileList.Items[sel]);
  627.           if idx >=0 then begin {in Auto List}
  628.             AutoList.Items.Delete(idx); {so remove it}
  629.             {now find which page contains this file...}
  630.             idx := NoteBook.GetIndexForPage(FileList.Items[sel]);
  631.             NoteBook.Pages.Delete(idx); {...and remove the page}
  632.           end;
  633.           FileList.Items.Delete(sel); {remove from FileList}
  634.         end;
  635.       end;
  636.     AdjustSizes; {adjust setup components}
  637.     if not NoteBook.Showing then NoteBook.Show; {make visible again}
  638.   end;
  639. end;
  640.  
  641. {Handles click on Remove menu item in Popup for TMemos}
  642. procedure TForm1.mEditRemoveClick(Sender: TObject);
  643. begin
  644.   {see if file modified and save if necessary}
  645.   if CheckSave(NoteBook.PageIndex, True) = mrCancel then Exit;
  646.   {delete currently active file from Autolist}
  647.   AutoList.Items.Delete(AutoList.Items.IndexOf(NoteBook.ActivePage));
  648.   {remove page from notebook}
  649.   NoteBook.Pages.Delete(NoteBook.PageIndex);
  650.   AdjustSizes;
  651. end;
  652.  
  653. {handles Edit menu item from either list box}
  654. procedure TForm1.mEditClick(Sender: TObject);
  655. var
  656.   List: TListBox;
  657.   idx: integer;
  658. begin
  659.   {set List to calling listbox. Simplifies checking}
  660.   if Sender = mAutoEdit then List := AutoList
  661.   else List := FileList;
  662.   if List.SelCount <> 1 then begin {can only edit one file at a time}
  663.     MessageBeep(mb_IconInformation);
  664.     MessageDlg('Please select a single item to Edit',
  665.       mtInformation, [mbok], 0);
  666.   end
  667.   else begin {make this page current}
  668.     idx :=  AutoList.Items.IndexOf(List.Items[List.ItemIndex]);
  669.     if idx<0 then begin {need to add file to AutoList and NoteBook}
  670.       bTakeClick(Sender); {fake click on Take button}
  671.       idx := NoteBook.Pages.Count - 2; {set focus to new page}
  672.     end;
  673.     NoteBook.PageIndex := idx; {set as current page}
  674.     NoteBookClick(Sender); {focus to the TMemo}
  675.   end;
  676. end;
  677.  
  678. {For the Exit menu item, simply close the application}
  679. procedure TForm1.mExitClick(Sender: TObject);
  680. begin
  681.   Close;
  682. end;
  683.  
  684. {Handles clicks on the Find menu item}
  685. procedure TForm1.mFindClick(Sender: TObject);
  686. begin
  687.   with TMemo(TTabPage(NoteBook.Pages.Objects[NoteBook.PageIndex])
  688.        .Controls[0]) do
  689.     if SelLength > 0 then {highlighted text...}
  690.       FindDialog1.FindText := SelText; {... so use as default find text}
  691.   FindDialog1.Execute; {display find dialog}
  692. end;
  693.  
  694. {Handles clicks on the Replace menu item}
  695. procedure TForm1.mReplaceClick(Sender: TObject);
  696. begin
  697.   with TMemo(TTabPage(NoteBook.Pages.Objects[NoteBook.PageIndex])
  698.        .Controls[0]) do
  699.     if SelLength > 0 then
  700.       ReplaceDialog1.FindText := SelText;
  701.   ReplaceDialog1.Execute;
  702. end;
  703.  
  704. {This is the search engine. It's used by both the Find and Replace
  705.  Dialogs}
  706. function TForm1.DoFind(Sender: TFindDialog): Boolean;
  707. var
  708.   TLen: Integer;
  709.   Tx, TxPos, FStr: PChar;
  710. begin
  711.   with TMemo(TTabPage(NoteBook.Pages.Objects[NoteBook.PageIndex])
  712.        .Controls[0]) do begin
  713.     TLen := GetTextLen + 1; {length of TMemo's text + 1 for #0}
  714.     Tx := StrAlloc(TLen);   {get memory from Windows}
  715.     GetTextBuf(Tx, TLen);   {get the entire text from the TMemo}
  716.     FStr := StrAlloc(Length(Sender.FindText) + 1); {memory for find text}
  717.     StrPCopy(FStr, Sender.FindText); {convert pascal find text to ASCIIZ}
  718.     if not (frMatchCase in Sender.Options) then begin
  719.       StrUpper(Tx); StrUpper(FStr); {so case doesn't matter}
  720.     end;
  721.     TxPos := Tx + SelStart + SelLength; {start search at caret position}
  722.     TxPos := StrPos(TxPos, Fstr);   {look for the string}
  723.     if TxPos <> nil then begin      {Found it!}
  724.       SelStart := TxPos - Tx;       {Set caret to found string}
  725.       SelLength := Length(Sender.FindText); {highlight it!}
  726.       Result := True; {let caller know it was found}
  727.     end else
  728.       Result :=False; {string not found}
  729.   end;
  730. end;
  731.  
  732. {Handle click on FindNext button (on either Find or Replace dialogs)}
  733. procedure TForm1.FindDialog1Find(Sender: TObject);
  734. begin
  735.   if not DoFind(TFindDialog(Sender)) then {string not found}
  736.     ShowMessage(''''+ TFindDialog(Sender).FindText +''' not found');
  737. end;
  738.  
  739. {Handle click on Replace or Replace All buttons}
  740. procedure TForm1.ReplaceDialog1Replace(Sender: TObject);
  741. var
  742.   reps: Integer;
  743. begin
  744.   reps := 0;
  745.   with TMemo(TTabPage(NoteBook.Pages.Objects[NoteBook.PageIndex])
  746.        .Controls[0]) do begin
  747.     {if highlighted text doesn't match search string do initial search}
  748.     if SelText <> ReplaceDialog1.FindText then
  749.       DoFind(TFindDialog(Sender));
  750.     {if highlighted text DOES = search string, do initial replace}
  751.     if SelText = ReplaceDialog1.FindText then begin
  752.       Seltext := ReplaceDialog1.ReplaceText;
  753.       Inc(reps);
  754.     end;
  755.     {see if Replace All button clicked}
  756.     if frReplaceAll in ReplaceDialog1.Options then begin
  757.       {search for all matches from current caret position}
  758.       while DoFind(TFindDialog(Sender)) do begin
  759.         {replace each one}
  760.         Seltext := ReplaceDialog1.ReplaceText;
  761.         Inc(reps);
  762.       end;
  763.       if reps > 1 then {show how many were done}
  764.         ShowMessage('Performed ' + IntToStr(reps) + ' replacements');
  765.     end;
  766.     if reps = 0 then
  767.       ShowMessage(''''+ TFindDialog(Sender).FindText +''' not found');
  768.   end;
  769. end;
  770.  
  771. {executes the Options dialog}
  772. procedure TForm1.bOptionsClick(Sender: TObject);
  773. begin
  774.   OptionsDlg.ShowModal;
  775. end;
  776.  
  777. {handles clicks on the protect menu items}
  778. procedure TForm1.ProtectClick(Sender: TObject);
  779. var
  780.   List: TListBox;
  781.   i: integer;
  782. begin
  783.   if Sender = mEditProtect then {from a TMemo}
  784.     ProtectDlg.ProtList.Lines.Add(NoteBook.ActivePage)
  785.   else begin {from a list box}
  786.     if Sender = mAutoProtect then List := AutoList
  787.     else List := FileList;
  788.     for i := 0 to List.Items.Count - 1 do begin
  789.       if List.Selected[i] then {add all selected items}
  790.         ProtectDlg.ProtList.Lines.Add(List.Items[i]);
  791.     end;
  792.   end;
  793. end;
  794.  
  795. {this handles a Windows message that lets us tell Windows the
  796.  minimum size it should allow our app to be sized by the user}
  797. procedure TForm1.WMGetMinMaxInfo(var msg: TWMGetMinMaxInfo);
  798. var
  799.   pt1, pt2: TPoint;
  800.   ht: integer;
  801. begin
  802.   {first time we get this message, form1 controls aren't yet created}
  803.   if BtnPanel <> nil then begin
  804.     {map bottom of help button to screen coordinate}
  805.     pt1 := bHelp.ClientToScreen(Point(0, bHelp.Height));
  806.     {calc distance from top of Form1, add height of lower panel + extra}
  807.     ht := pt1.y - Top + BottomPanel.Height + 16;
  808.     {that's minimum needed to keep help button in view}
  809.     msg.MinMaxInfo^.ptMinTrackSize.y := ht;
  810.     {keep reasonable width for 3 tab columns in notebook}
  811.     msg.MinMaxInfo^.ptMinTrackSize.x := 301;
  812.     msg.result := 0;
  813.   end else
  814.     msg.result := 1;
  815. end;
  816.  
  817. {if the Delete option on the Options Dialog changes, OptionDlg
  818.  sends a message to Form1 so the Delete menu items on the pop-up
  819.  menus can be disabled or enabled as appropriate}
  820. procedure TForm1.WMDelOptChanged(var msg: TMessage);
  821. begin
  822.   {set Delete menu item on Edit popup to enabled if deletes allowed}
  823.   mEditDelete.Enabled := (OptionsDlg.DeleteGroup.ItemIndex <> opNoDelete);
  824.   {set Delete menu item on listbox popups to same value}
  825.   mFileDelete.Enabled := mEditDelete.Enabled;
  826.   mAutoDelete.Enabled := mEditDelete.Enabled;
  827. end;
  828.  
  829. var
  830.   {a temporary variable. Only used in initialization code below}
  831.   strz: pchar;
  832.  
  833. {All units can optionally have an initialization section at the end.
  834.  This section (in each unit, and in the order the units are referenced
  835.  in the DPR's Uses clause) executes before the main code of the program
  836.  (in project's DPR). This allows an opportunity to set certain things up
  837.  such as global resources.}
  838. procedure TForm1.bHelpClick(Sender: TObject);
  839. begin
  840.   Application.HelpCommand(Help_Contents, 0);
  841. end;
  842.  
  843. initialization
  844.   {allocate enough memory to hold longest possible path name}
  845.   strz := StrAlloc(145);
  846.   {get the system's Windows directory path}
  847.   GetWindowsDirectory(strz, 145);
  848.   {Append a '\' to it so file names can be appended}
  849.   WinDir := StrPas(strz) + Slash;
  850.   {dispose the temporary memory}
  851.   StrDispose(strz);
  852.   {create a TIniFile object to handle our own INI file}
  853.   SysIni := TIniFile.Create(WinDir +
  854.     ChangeFileExt(ExtractFileName(Application.ExeName),'.INI'));
  855. end.
  856.